import identity from "../../../utils/identity";
import KeyCodes from "../../../utils/key-codes";
import startCase from "../../../utils/startcase";
import { getComponentConfig } from "../../../utils/config";
import { htmlOrText } from "../../../utils/html";
import { isUndefinedOrNull } from "../../../utils/inspect";
import filterEvent from "./filter-event";
import textSelectionActive from "./text-selection-active";
import { NlyThead } from "../thead";
import { NlyTfoot } from "../tfoot";
import { NlyTr } from "../tr";
import { NlyTh } from "../th";

export default {
  props: {
    headVariant: {
      type: String, // 'light', 'dark' or `null` (or custom)
      default: () => getComponentConfig("NlyTable", "headVariant")
    },
    headRowVariant: {
      type: String, // Any Bootstrap theme variant (or custom)
      default: null
    },
    theadClass: {
      type: [String, Array, Object]
      // default: undefined
    },
    theadTrClass: {
      type: [String, Array, Object]
      // default: undefined
    }
  },
  methods: {
    fieldClasses(field) {
      // Header field (<th>) classes
      return [
        field.class ? field.class : "",
        field.thClass ? field.thClass : ""
      ];
    },
    headClicked(evt, field, isFoot) {
      if (this.stopIfBusy && this.stopIfBusy(evt)) {
        // If table is busy (via provider) then don't propagate
        return;
      } else if (filterEvent(evt)) {
        // Clicked on a non-disabled control so ignore
        return;
      } else if (textSelectionActive(this.$el)) {
        // User is selecting text, so ignore
        /* istanbul ignore next: JSDOM doesn't support getSelection() */
        return;
      }
      evt.stopPropagation();
      evt.preventDefault();
      this.$emit("head-clicked", field.key, field, evt, isFoot);
    },
    renderThead(isFoot = false) {
      const h = this.$createElement;
      const fields = this.computedFields || [];

      if (this.isStackedAlways || fields.length === 0) {
        // In always stacked mode, we don't bother rendering the head/foot
        // Or if no field headings (empty table)
        return h();
      }

      // Reference to `selectAllRows` and `clearSelected()`, if table is selectable
      const selectAllRows = this.isSelectable ? this.selectAllRows : () => {};
      const clearSelected = this.isSelectable ? this.clearSelected : () => {};

      // Helper function to generate a field <th> cell
      const makeCell = (field, colIndex) => {
        let ariaLabel = null;
        if (!field.label.trim() && !field.headerTitle) {
          // In case field's label and title are empty/blank
          // We need to add a hint about what the column is about for non-sighted users
          /* istanbul ignore next */
          ariaLabel = startCase(field.key);
        }
        const hasHeadClickListener =
          this.hasListener("head-clicked") || this.isSortable;
        const handlers = {};
        if (hasHeadClickListener) {
          handlers.click = evt => {
            this.headClicked(evt, field, isFoot);
          };
          handlers.keydown = evt => {
            const keyCode = evt.keyCode;
            if (keyCode === KeyCodes.ENTER || keyCode === KeyCodes.SPACE) {
              this.headClicked(evt, field, isFoot);
            }
          };
        }
        const sortAttrs = this.isSortable
          ? this.sortTheadThAttrs(field.key, field, isFoot)
          : {};
        const sortClass = this.isSortable
          ? this.sortTheadThClasses(field.key, field, isFoot)
          : null;
        const sortLabel = this.isSortable
          ? this.sortTheadThLabel(field.key, field, isFoot)
          : null;
        const data = {
          ref: field.ref,
          refInFor: true,
          key: field.key,
          class: [this.fieldClasses(field), sortClass],
          props: {
            variant: field.variant,
            stickyColumn: field.stickyColumn
          },
          style: field.thStyle || {},
          attrs: {
            // We only add a tabindex of 0 if there is a head-clicked listener
            tabindex: hasHeadClickListener ? "0" : null,
            abbr: field.headerAbbr || null,
            title: field.headerTitle || null,
            "aria-colindex": colIndex + 1,
            "aria-label": ariaLabel,
            ...this.getThValues(
              null,
              field.key,
              field.thAttr,
              isFoot ? "foot" : "head",
              {}
            ),
            ...sortAttrs
          },
          on: handlers
        };
        // Handle edge case where in-document templates are used with new
        // `v-slot:name` syntax where the browser lower-cases the v-slot's
        // name (attributes become lower cased when parsed by the browser)
        // We have replaced the square bracket syntax with round brackets
        // to prevent confusion with dynamic slot names
        let slotNames = [
          `head(${field.key})`,
          `head(${field.key.toLowerCase()})`,
          "head()"
        ];
        if (isFoot) {
          // Footer will fallback to header slot names
          slotNames = [
            `foot(${field.key})`,
            `foot(${field.key.toLowerCase()})`,
            "foot()",
            ...slotNames
          ];
        }
        const scope = {
          label: field.label,
          column: field.key,
          field,
          isFoot,
          // Add in row select methods
          selectAllRows,
          clearSelected
        };
        const content =
          this.normalizeSlot(slotNames, scope) ||
          (field.labelHtml
            ? h("div", { domProps: htmlOrText(field.labelHtml) })
            : field.label);
        const srLabel = sortLabel
          ? h("span", { staticClass: "sr-only" }, ` (${sortLabel})`)
          : null;
        // Return the header cell
        return h(NlyTh, data, [content, srLabel].filter(identity));
      };

      // Generate the array of <th> cells
      const $cells = fields.map(makeCell).filter(identity);

      // Genrate the row(s)
      const $trs = [];
      if (isFoot) {
        const trProps = {
          variant: isUndefinedOrNull(this.footRowVariant)
            ? this.headRowVariant
            : this.footRowVariant
        };
        $trs.push(
          h(NlyTr, { class: this.tfootTrClass, props: trProps }, $cells)
        );
      } else {
        const scope = {
          columns: fields.length,
          fields: fields,
          // Add in row select methods
          selectAllRows,
          clearSelected
        };
        $trs.push(this.normalizeSlot("thead-top", scope) || h());
        $trs.push(
          h(
            NlyTr,
            {
              class: this.theadTrClass,
              props: { variant: this.headRowVariant }
            },
            $cells
          )
        );
      }

      return h(
        isFoot ? NlyTfoot : NlyThead,
        {
          key: isFoot ? "nly-tfoot" : "nly-thead",
          class: (isFoot ? this.tfootClass : this.theadClass) || null,
          props: isFoot
            ? { footVariant: this.footVariant || this.headVariant || null }
            : { headVariant: this.headVariant || null }
        },
        $trs
      );
    }
  }
};
